# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
import math, time, socket
from raytrace import crossed, calc_unit

class InitInfo:
    def __init__(self, init_data):
        if init_data[0] <> 'I':
            raise Exception("It's not init data")
        data = init_data[1]
        self.map_x        = float(data[0]) # span of the map's x-axis(meters)
        self.map_y        = float(data[1]) # span of the map's y-axis(meters)
        self.time_limit   = int(data[2])   # time limit for the map (milliseconds)
        self.min_sensor   = float(data[3]) # minimum range of the vehicle's visual sensors (meters)
        self.max_sensor   = float(data[4]) # maximum range of the vehicle's visual sensors (meters)
        self.max_speed    = float(data[5]) # maximum speed of the vehicle (meters per second)
        self.max_turn     = float(data[6]) # maximum rotational speed when turning (degrees per second)
        self.max_had_turn = float(data[7]) # maximum rotational speed when turning hard (degrees per second)

def arr_getter(pos):
    def getter(self):
        return self.data[pos]
    return getter
def to_float(lmbd):
    def getter(self):
        return float(lmbd(self))
    return getter
def to_int(lmbd):
    def getter(self):
        return int(lmbd(self))
    return getter
class Telemetry:
    def __init__(self, init_data = None):
        if init_data is None:
            init_data = ['T', [None]*6]
        self.data = init_data[1]

    def set_data(self, init_data):
        if init_data[0] <> 'T':
            raise Exception("It's not telemetry data")
        self.data = init_data[1]

    time_stamp    = property(fget=to_int(arr_getter(0)))   # number of milliseconds since the start of the run
    vehicle_ctl   = property(fget=arr_getter(1))           # current state of the vehicle controls
    vehicle_x     = property(fget=to_float(arr_getter(2))) # x-coordinate of the vehicle's current position
    vehicle_y     = property(fget=to_float(arr_getter(3))) # y-coordinate of the vehicle's current position
    vehicle_dir   = property(fget=to_float(arr_getter(4))) # direction of the vehicle measured as a counterclockwise angle from the x-axis
    vehicle_speed = property(fget=to_float(arr_getter(5))) # vehicle's current speed (meters per second)

    def get_objects(self):
        return self.data[5:]

    objects = property(fset=get_objects)

def testTelemetry():
    t = Telemetry(('T', '0 -- -25.000 25.000 50.0 0.000 h 0.000 0.000 5.000 b -45.312 64.062 0.600 b -31.250 68.750 0.996 b -42.188 57.812 0.401 b -28.125 59.375 0.523 b -18.750 68.750 1.097 b -18.750 56.250 2.124 b -7.812 54.688 0.587 '.split(' ')))
    assert t.vehicle_x   == -25
    assert t.vehicle_y   ==  25
    assert t.vehicle_dir ==  50

class Rover:
    def __init__(self, init_info):
        self.states  = []
        self.current = None
        self.target_x = 0
        self.target_y = 0
        self.a        = 5
        self.k        = 1

    def _correct_params(self):
        pass

    def process_telemety(self,t):
        self.states.append(t)
        self.current = t
        if len(self.states) > 3:
            self.states = self.states[1:]
            self._correct_params()
        
    def get_next_command(self):        
        dy = self.target_y - self.current.vehicle_y
        dx = self.target_x - self.current.vehicle_x
        deg = math.atan2(abs(dy), abs(dx))/math.pi*180 
        
        if dx>0 and dy<0 and deg<90:
            deg = 360-deg
        elif dx<0 and dy>0:
            deg = 180-deg
        elif dx<0 and dy<0:
            deg = 180+deg
        
        if self.current.vehicle_dir < 0:
            self.current.vehicle_dir += 360
        
        ddeg = min(abs(deg-self.current.vehicle_dir),
                   abs(360-deg+self.current.vehicle_dir))
        if ddeg>75:
            if abs(deg-self.current.vehicle_dir)  < abs(360-deg+self.current.vehicle_dir):
                if deg < self.current.vehicle_dir:
                    return 'r;'
                else:
                    return 'l;'
            else:
                if deg < self.current.vehicle_dir:
                    return 'l;'
                else:
                    return 'r;'

        distance = (self.current.vehicle_y - self.target_y)**2 + (self.current.vehicle_x - self.target_x)**2
        if self.current.vehicle_speed*20 < distance:
            return 'a;'
        elif self.current.vehicle_speed*5 > distance:
            return 'b;'
        
        return ';'

    def set_goal(self, goal):
        self.target_x, self.target_y = goal

class Map:
    def __init__(self, size_x, size_y, target_width):
        self.size_x = size_x
        self.size_y = size_y
        self.objects = {}

    def add_map_object(self, type, pos_x, pos_y, r):
        if (pos_x,pos_y) not in self.objects:
            self.objects[(pos_x,pos_y)] = (type, r)

class Brain:
    def __init__(self, rover, init_data):
        self.rover = rover;
        self.last_telemetree = datetime.now()
        self.map = Map(init_data.map_x, init_data.map_y, init_data.min_sensor)

    def has_telemetree(self):
        return datetime.now() - self.last_telemetree >= timedelta(milliseconds=100)

    def get_goal(self):
        return (0,0)

    def process_telemety(self,t):
        pass
    
    def calc_rays(self):
        for angle in [0, 30, -30, 60, -60]:
            angle_score = angle/180 * 30            
            pass
    
class TestRadio():
    pass

class SocketRadio():
    def __init__(self,host,port):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.connect((host, port))
        self.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY,1)
        self.s.setblocking(1)

    def read_responce(self):
        cmd_type = self.s.recv(2)[:1]
        cmd_str = ''
        while True:
            ch = self.s.recv(1)
            if ch == ';':
                break
            cmd_str += ch
        return (cmd_type,cmd_str[:-1].split(' '))

    def write_answer(self, answer):
        self.s.send(answer)
    
    def close(self):
        self.s.close()

def main(host, port):
    try:
        radio      = SocketRadio(host, port)
        init_info  = InitInfo(radio.read_responce())
        rower      = Rover(init_info)
        brain      = Brain(rower, init_info)
                
        dt = radio.read_responce()
        t = Telemetry(dt)
        rower.process_telemety(t)
        brain.process_telemety(t)
        rower.set_goal(brain.get_goal())
        radio.write_answer('a;')
        while True:            
            command = rower.get_next_command()

            if brain.has_telemetree():
                dt = radio.read_responce()
                if dt[0] == 'T':
                    t = Telemetry(dt)
                    brain.process_telemety(t)
                    rower.process_telemety(t)
                    rower.set_goal(brain.get_goal())
                elif dt[0] == 'E':
                    brain.clear()
                    rower.clear()
                    rower.set_goal(brain.get_goal())

            radio.write_answer(command)            

    except KeyboardInterrupt:
        radio.close()
    except:
        radio.close()
        raise

#if __name__ == '__main__':
#    import sys
#    main(sys.argv[1], int(sys.argv[2]))
